package main;

import java.util.LinkedList;

public class Stage extends Main {

	LinkedList<Instruction> stageInstructions;
	
	public Stage() {
		this.stageInstructions = new LinkedList<Instruction>();

	}
	
	public boolean empty() {
		
		return stageInstructions.isEmpty();
		
	}

	static boolean executeFetch() {

		Fetch.stageInstructions.clear();
		
		if(!MemoryUnit.fecthFromMemory()) return true;

		for (int i = 0; i<ways && !MemoryUnit.finst.isEmpty() ; i++) {
			
			Instruction ins = MemoryUnit.finst.removeFirst();
			instructions.add(ins);
			Fetch.stageInstructions.add(ins);
			inst_buffer.add(ins);
			ins.start = cycles_spanned;
			
		}
		return true;

	}

	static boolean executeDecode(boolean f2) {

		Decode.stageInstructions.clear();
		
		if(!f2) return false;
				
		if (freeListSize - usedPhysicalRegisters.size() < 1)
			return false;
		if (res_stat_num - reserve < 1)
			return false;
		if (ROBSize - reserveROB < 1)
			return false;
		
		if(BranchUnit.stall) {
			return false;
		}
		
		
		for (int i = 0; i < ways && IBIndex<inst_buffer.size(); i++) {

			Instruction ins = inst_buffer.get(IBIndex++);
			if(Fetch.stageInstructions.contains(ins)) {
				IBIndex--;
				break;
			}
			Decode.stageInstructions.add(ins);
			rename(ins);
			reserve++;
			reserveROB++;
			if(reserve == res_stat_num || reserveROB == ROBSize) break;
			
		}

		return true;
	}

	static void rename(Instruction ins) {

		Integer r;

		r = RegMapping.get(ins.source1);
		if (r != null)
			ins.source1 = r;
		r = RegMapping.get(ins.source2);
		if (r != null)
			ins.source2 = r;
		
		if(ins.dest == -1) {
			reserveROB--; 
			return;
		}
		
		for (int i = 0; i < freeListSize; i++) {

			if (usedPhysicalRegisters.contains(freeList_index)) {
				freeList_index++;
				if (freeList_index == freeListSize + 1)
					freeList_index = 8;

			} else
				break;

		}

		r = freeList_index;

		RegMapping.put(ins.dest, r);
		usedPhysicalRegisters.add(r);
		ins.dest = r;

	}
	
	static boolean hasFunctionalUnit(Instruction e) {
		
		if(e.isMemory() || e.isBranch()) return true;
		//if(e.isBranch()) return BranchUnit.inst.isEmpty();
		
		int count = 0;
		
		for(int i=0; i<ReservationSation.size(); i++) {
			Instruction ee = ReservationSation.get(i).inst;
			if(e.type.equals(ee.type)) count++;
			if(e.type.equals("ADD") && ee.type.equals("ADDI")) count++;
			if(e.type.equals("ADDI") && ee.type.equals("ADD")) count++;
		}
		
		int n = 0;
		
		if(e.type.equals("ADD") || e.type.equals("ADDI")) n = num_add;
		else if(e.type.equals("NAND")) n = num_nand;
		else if(e.type.equals("MUL")) n = num_mul;
		else n = num_div;
		
		return count != n; 
		
	}

	static boolean executeDispatch(boolean f) {
		boolean flagr1 = false, flagr2 = false;
		Instruction i;
		Integer r1 = 0, r2 = 0;
		
		Dispatch.stageInstructions.clear();
		
		if(BranchUnit.stall) {
			BranchUnit.stall = false;
			//System.out.println("stall");
			return false;
		}
		
		int limit = IBIndex;
		
		for (int ii = 0; ii < Math.min(ways,limit) && !inst_buffer.isEmpty(); ii++) {
			
			flagr1 = flagr2 = false;
			r1 = r2 = 0;
			
			i = inst_buffer.getFirst();
			
			if(Fetch.stageInstructions.contains(i) 
				|| Decode.stageInstructions.contains(i) 
				|| !hasFunctionalUnit(i)) break;
			
			i = inst_buffer.removeFirst();
			IBIndex--;
			Dispatch.stageInstructions.add(i);
			
			Order k = new Order(ROB.size());
			
			if(!i.type.equals("SW") && (!i.isBranch() || i.type.equals("JALR"))){
				ROB.add(new ROBEntry(i, false, i.destOriginal, 0, k));
				RegRobMapping.put(i.dest, k);
			}
			
			if(i.isBranch() && !i.type.equals("JALR"))
				ROB.add(new ROBEntry(i, false, i.destOriginal, 0, k));
			
			// For source 1
			Order j = RegRobMapping.get(i.source1);

			if (j == null) {
				r1 = RegFile[i.source1];
				flagr1 = true;
			}

			else {
				if (ROB.get(j.value).ready) {
					r1 = ROB.get(j.value).value;
					flagr1 = true;
				}
			}
			
			//For source 2
			Order t = RegRobMapping.get(i.source2);

			if (t == null) {
				r2 = RegFile[i.source2];
				flagr2 = true;
			}

			else {
				if (ROB.get(t.value).ready) {
					r2 = ROB.get(t.value).value;
					flagr2 = true;
				}
			}

			ReservationSation.add(new ResStationEntry(i, flagr1, flagr2, r1, r2));

		}

		return true;

	}

	static Instruction executeIssue() {
		
		Issue.stageInstructions.clear();
		
		Instruction ins = null;
		
		for(int i=0; i<ReservationSation.size(); i++) {
			
			ResStationEntry e = ReservationSation.get(i);
			
			if(e.r1 && e.r2 && !e.InExecution && e.ready) {
				ins = e.inst;
				ins.v1 = e.v1;
				ins.v2 = e.v2;
				e.InExecution = true;
				break;
			}
			
			
		}
		
		for(int i=0; i<ReservationSation.size(); i++) {
			ResStationEntry e = ReservationSation.get(i);
			if(!e.ready) e.ready = true;
		}
				
		if(ins != null) {
			Issue.stageInstructions.add(ins);
			if(ins.isArithmetic()) sendToFs(ins);
			else if(ins.isMemory()) MemoryUnit.inst.add(ins);
			else BranchUnit.inst.add(ins);
			return ins;
		}
		
				
		return null;

	}

	static void sendToFs(Instruction ins) {
		
		//search for Functional Unit with minimum load
		FunctionalUnit f = null;
		FunctionalUnit f2 = null;
		
		LinkedList<FunctionalUnit> fsOp = new LinkedList<FunctionalUnit>();
		
		for(int i=0; i<FS.size(); i++) {
			
			f = FS.get(i);
			
			if(f.map(ins.type)) fsOp.add(f);
		}
		
		f = fsOp.getFirst();
		
		for(int i=1; i<fsOp.size(); i++) {
			
			f2 = fsOp.get(i);
			if(f2.instructions.size() < f.instructions.size()) f = f2;
			
		}
		
		f.instructions.add(ins);
				
	}
	
	static boolean executeExecute() {
		
		for(int i=0; i<FS.size(); i++) {
			
			FS.get(i).execute();
			
		}
		
		MemoryUnit.execute();
		BranchUnit.execute();
		
		return true;

	}

	static boolean executeCommit() {
		
		Commit.stageInstructions.clear();
		
		if(!ROB.isEmpty()) {
			
			ROBEntry e = ROB.getFirst();
			Commit.stageInstructions.add(e.inst);
			
			if(e.ready) {
				if(e.regName!=-1) RegFile[e.regName] = e.value;
				RegRobMapping.remove(e.inst.dest);
				if(e.regName!=-1 
					&& RegMapping.get(e.inst.destOriginal) == e.inst.dest){
					RegMapping.remove(e.inst.destOriginal);
				}
				reserveROB--;
				ROB.removeFirst();
				usedPhysicalRegisters.remove(new Integer(e.inst.dest));
				for(int i=0; i<ROB.size(); i++) ROB.get(i).order.value--;
				e.inst.finished = true;
				e.inst.noOfCycles++;
				e.inst.finish = cycles_spanned;
				
				recoverRenaming(e);
			}
			
		}
		
		LinkedList<Instruction> li = Execute.stageInstructions;
		
		while(!li.isEmpty()) {
			
			if(!li.getFirst().isBranch()){
			int robIndex = RegRobMapping.get(li.removeFirst().dest).value;
			ROB.get(robIndex).ready = true;
			}
			
			else {
				Instruction br = li.removeFirst();
				for(int k=0; k<ROB.size(); k++)
					if(ROB.get(k).inst == br) ROB.get(k).ready=true;
			}
		}
		
		return true;

	}
	
	private static void recoverRenaming(ROBEntry e) {
		
		LinkedList<Instruction> li = inst_buffer;
		
		for(int i=0; i<li.size(); i++) {
			
			Instruction ee = li.get(i);
			if(ee.source1 == e.inst.dest) ee.source1 = e.regName;
			if(ee.source2 == e.inst.dest) ee.source2 = e.regName;
			
		}
		
	}

	static void printExecute() {
		
		for(int i=0; i<FS.size(); i++) {
			//System.out.println("yarab");
			FunctionalUnit fs = FS.get(i);
			LinkedList<Instruction> li = fs.instructions;
			
			for(int j=0; j<li.size(); j++) {
				
				Instruction ins = li.get(j); 
				System.out.println(ins.format+"  N = "+ins.N + " ; "+ins.dest+","+ins.source1+","+ins.source2);
			
				
			}
			
			
		}
		LinkedList<Instruction> li = MemoryUnit.inst;
		for(int i=0; i<li.size(); i++) {
			Instruction ins = li.get(i); 
			System.out.println(ins.format+"  N = "+ins.N + " ; "+ins.dest+","+ins.source1+","+ins.source2);
		
		}
		
		li = BranchUnit.inst;
		for(int i=0; i<li.size(); i++) {
			Instruction ins = li.get(i); 
			System.out.println(ins.format+"  N = "+ins.N + " ; "+ins.dest+","+ins.source1+","+ins.source2);
		
		}
		
	}

}
